﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;

using Cards.Util;
using Cards.Objects;

namespace Cards.GUI
{
    public partial class FrmCreateMultiGame : Form
    {
        private ArrayList m_arrLabelPlayerLabel;
        private ArrayList m_arrLabelPlayerName;
        private ArrayList m_arrCbxPlayerType;
        private ArrayList m_availableId; // Hold ValueMember of ComboBoxs cbPlayerType

        private MultiGame m_multiGame;
        private Timer m_msgTimer;

        private MULTI_PLAYER_TYPE m_multiType;
        private int m_gamePlayerId;

        private bool m_isStartedGame;
        private bool m_bFormLoaded;


        public int[] m_pcArrayCards;
        public int m_pcIndex;


        public ArrayList ARRCOMBOPLAYERTYPE
        {
            get { return m_arrCbxPlayerType; }
            set { m_arrCbxPlayerType = value; }
        }
        public ArrayList ARRLABELPLAYERNAME
        {
            get { return m_arrLabelPlayerName; }
            set { m_arrLabelPlayerName = value; }
        }
        public ArrayList ARRLABELPLAYERLABEL
        {
            get { return m_arrLabelPlayerLabel; }
            set { m_arrLabelPlayerLabel = value; }
        }
        public MultiGame MULTIGAME
        {
            get { return m_multiGame; }
            set { m_multiGame = value; }
        }
        public ArrayList AVAILABLEID
        {
            get { return m_availableId; }
            set { m_availableId = value; }
        }
        public int GAMEPLAYERID
        {
            get { return m_gamePlayerId; }
            set { m_gamePlayerId = value; }
        }
        public bool ISSTARTEDGAME
        {
            get { return m_isStartedGame; }
            set { m_isStartedGame = value; }
        }


        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="_multiType"></param>
        public FrmCreateMultiGame(MULTI_PLAYER_TYPE _multiType)
        {
            m_multiType = _multiType;
            m_bFormLoaded = false;
            m_gamePlayerId = -1;
            m_isStartedGame = false;

            InitializeComponent();
            InitCtrls();
        }

        /// <summary>
        /// Init form controls
        /// </summary>
        private void InitCtrls()
        {
            m_arrLabelPlayerLabel = new ArrayList();
            m_arrLabelPlayerName = new ArrayList();
            m_arrCbxPlayerType = new ArrayList();
            m_availableId = new ArrayList();

            m_msgTimer = new Timer();
            m_msgTimer.Interval = 3000;
            m_msgTimer.Tick += new System.EventHandler(msgTimer_Tick);

            UpdateControls(false);
        }

        /// <summary>
        /// Init object for server mode
        /// </summary>
        void InitServer()
        {
            m_multiGame = new GameServer(this, MainFrame.s_numOfPlayers - 1);
            m_gamePlayerId = 1;
            MainFrame.s_isServer = true;

            btnConnectServer.Visible = false;
            btnDisconnectFromServer.Visible = false;
            btnCreateServer.Visible = true;
            btnStopServer.Visible = true;
        }

        /// <summary>
        /// Init object for client mode
        /// </summary>
        void InitClient()
        {
            m_multiGame = new GameClient(this);
            MainFrame.s_isServer = false;

            btnConnectServer.Visible = true;
            btnDisconnectFromServer.Visible = true;
            btnCreateServer.Visible = false;
            btnStopServer.Visible = false;
        }

        /// <summary>
        /// Load form method
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FrmCreateGameServer_Load(object sender, EventArgs e)
        {
            InitCtrlArray();

            if (m_multiType == MULTI_PLAYER_TYPE.MULTI_PLAYER_SERVER)
            {
                InitServer();
                DisplayServerControls();
            }
            else if (m_multiType == MULTI_PLAYER_TYPE.MULTI_PLAYER_CLIENT)
            {
                InitClient();
                DisplayClientControls();
            }
            m_bFormLoaded = true;
        }

        /// <summary>
        /// Display corresponding controls in server mode
        /// </summary>
        void DisplayServerControls()
        {
            for (int i = 0; i < MainFrame.s_numOfPlayers; i++)
            {
                ((Label)m_arrLabelPlayerLabel[i]).Visible = true;
                ((Label)m_arrLabelPlayerName[i]).Visible = true;
                ((ComboBox)m_arrCbxPlayerType[i]).Visible = true;
            }

            if (m_multiType == Util.MULTI_PLAYER_TYPE.MULTI_PLAYER_SERVER)
            {
                ((Label)m_arrLabelPlayerName[0]).Text = MainFrame.s_loginName;
                ((ComboBox)m_arrCbxPlayerType[0]).SelectedIndex = (int)PLAYER_TYPE.HUMAN;
            }

            ipServer.Text = m_multiGame.GetIP();
            lbTitle.Text = "MÁY CHỦ";

            //test code
            //ipServer.Text = "7.214.162.105";
            //txtServerPort.Text = "8000";
        }

        /// <summary>
        /// Display corresponding controls in client mode
        /// </summary>
        void DisplayClientControls()
        {
            ipServer.Text = m_multiGame.GetIP();
            lbTitle.Text = "MÁY CON";

            //test code
            //ipServer.Text = "118.69.224.11";
            //txtServerPort.Text = "8000";
        }

        /// <summary>
        /// Init the array to manage the controls
        /// </summary>
        private void InitCtrlArray()
        {
            m_arrLabelPlayerLabel.Add(lbLabel1);
            m_arrLabelPlayerLabel.Add(lbLabel2);
            m_arrLabelPlayerLabel.Add(lbLabel3);
            m_arrLabelPlayerLabel.Add(lbLabel4);
            m_arrLabelPlayerLabel.Add(lbLabel5);
            m_arrLabelPlayerLabel.Add(lbLabel6);
            m_arrLabelPlayerLabel.Add(lbLabel7);
            m_arrLabelPlayerLabel.Add(lbLabel8);
            m_arrLabelPlayerLabel.Add(lbLabel9);
            m_arrLabelPlayerLabel.Add(lbLabel10);

            m_arrLabelPlayerName.Add(lbPlayer1);
            m_arrLabelPlayerName.Add(lbPlayer2);
            m_arrLabelPlayerName.Add(lbPlayer3);
            m_arrLabelPlayerName.Add(lbPlayer4);
            m_arrLabelPlayerName.Add(lbPlayer5);
            m_arrLabelPlayerName.Add(lbPlayer6);
            m_arrLabelPlayerName.Add(lbPlayer7);
            m_arrLabelPlayerName.Add(lbPlayer8);
            m_arrLabelPlayerName.Add(lbPlayer9);
            m_arrLabelPlayerName.Add(lbPlayer10);

            m_arrCbxPlayerType.Add(cbPlayer1Type);
            m_arrCbxPlayerType.Add(cbPlayer2Type);
            m_arrCbxPlayerType.Add(cbPlayer3Type);
            m_arrCbxPlayerType.Add(cbPlayer4Type);
            m_arrCbxPlayerType.Add(cbPlayer5Type);
            m_arrCbxPlayerType.Add(cbPlayer6Type);
            m_arrCbxPlayerType.Add(cbPlayer7Type);
            m_arrCbxPlayerType.Add(cbPlayer8Type);
            m_arrCbxPlayerType.Add(cbPlayer9Type);
            m_arrCbxPlayerType.Add(cbPlayer10Type);

            for (int i = 0; i < m_arrCbxPlayerType.Count; i++)
            {
                ((ComboBox)m_arrCbxPlayerType[i]).Items.Add("Người");
                ((ComboBox)m_arrCbxPlayerType[i]).Items.Add("Máy");
                ((ComboBox)m_arrCbxPlayerType[i]).Items.Add("Không sử dụng");
                ((ComboBox)m_arrCbxPlayerType[i]).SelectedIndex = (int)PLAYER_TYPE.HUMAN;
            }

            for (int i = 1; i < MainFrame.s_numOfPlayers; i++)
            {
                m_availableId.Add(i + 1);
            }
            ArrayUtil.ASCSortArrayListInt(m_availableId);
        }

        /// <summary>
        /// Disable some control to reset state
        /// </summary>
        public void ResetControlsState()
        {
            if (m_multiGame != null)
            {
                m_multiGame.Stop();
            }

            for (int i = 0; i < m_arrLabelPlayerLabel.Count; i++)
            {
                ((Label)m_arrLabelPlayerLabel[i]).Visible = false;
                ((Label)m_arrLabelPlayerName[i]).Visible = false;
                ((ComboBox)m_arrCbxPlayerType[i]).Visible = false;
            }
        }

        #region Server part
        /// <summary>
        /// Create a game server
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCreateServer_Click(object sender, EventArgs e)
        {
            if (txtServerPort.Text == "")
            {
                MessageBox.Show("Please enter a Port Number");
                return;
            }

            m_multiGame.IP = ipServer.Text;
            m_multiGame.PORT = txtServerPort.Text;

            if (m_multiGame.Start())
            {
                UpdateControls(true);
                UpdateMsg("Máy chủ đã được tạo thành công");
            }
        }

        /// <summary>
        /// Stop server listenning
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStopServer_Click(object sender, EventArgs e)
        {
            UpdateControls(false);
            m_multiGame.Stop();
            UpdateMsg("Máy chủ đã dừng");
        }

        /// <summary>
        /// Provide a valid space id for a client game connection
        /// </summary>
        /// <returns></returns>
        public int GetAvailableClientId()
        {
            int res = -1;

            if (m_availableId.Count > 0)
            {
                ArrayUtil.ASCSortArrayListInt(m_availableId);
                res = (int)m_availableId[0];

                //give away the id
                m_availableId.Remove(res);
            }

            return res;
        }

        /// <summary>
        /// Give the id after client disconnect
        /// </summary>
        /// <returns></returns>
        public void GiveBackAvailableClientId(int _id)
        {
            if (_id > 1)
            {
                m_availableId.Add(_id);
                ArrayUtil.ASCSortArrayListInt(m_availableId);
            }
        }

        public void UpdatePlayerName(int _id, String _name)
        {
            try
            {
                if (_id > 0)
                {
                    MethodInvoker action = delegate
                    {
                        ((Label)m_arrLabelPlayerName[_id - 1]).Text = _name;
                    };

                    ((Label)m_arrLabelPlayerName[_id - 1]).BeginInvoke(action);
                }
            }
            catch (SystemException se)
            {
                MessageBox.Show(se.ToString());
            }
        }

        /// <summary>
        /// Return a string contains information of all players
        /// </summary>
        /// <returns></returns>
        public String GetStringPlayerList()
        {
            String str = "";

            this.Invoke((MethodInvoker)delegate()
            {
                for (int i = 0; i < MainFrame.s_numOfPlayers; i++)
                {
                    if (i > 0)
                    {
                        str += NetworkConstants.MESSAGETYPE_SEPARATOR;
                    }

                    str += ((ComboBox)m_arrCbxPlayerType[i]).ValueMember;
                    str += NetworkConstants.MESSAGETYPE_SUBSEPARATOR;
                    str += ((Label)m_arrLabelPlayerName[i]).Text;
                    str += NetworkConstants.MESSAGETYPE_SUBSEPARATOR;
                    str += ((ComboBox)m_arrCbxPlayerType[i]).SelectedIndex;
                }
            });

            return str;
        }

        #endregion Server part


        #region Client part

        /// <summary>
        /// Connect to a server in Client mode
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnConnectServer_Click(object sender, EventArgs e)
        {
            if ((txtServerPort.Text == "") || (ipServer.Text == "..."))
            {
                MessageBox.Show("Please enter a Port Number");
                return;
            }

            m_multiGame.IP = ipServer.Text;
            m_multiGame.PORT = txtServerPort.Text;

            if (m_multiGame.Start())
            {
                //UpdateControls(true);
                //UpdateMsg("Kết nối máy chủ thành công");
            }
            else
            {
                UpdateControls(false);
                UpdateMsg("Không thể kết nối máy chủ");
            }
        }

        /// <summary>
        /// Disconnect from the server in client mode
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnDisconnectFromServer_Click(object sender, EventArgs e)
        {
            UpdateControls(false);
            Disconnect();
            UpdateMsg("Ngắt kết nối với máy chủ");
        }

        /// <summary>
        /// Display a message in this dialog when connect game succeed
        /// </summary>
        /// <param name="_str"></param>
        public void OnConnectGameSucceed()
        {
            MethodInvoker action = delegate
            {
                UpdateControls(true);
                UpdateMsg("Đã kết nối trò chơi");
            };
            this.BeginInvoke(action);
        }

        public void OnReceivePlayerList(ArrayList _list)
        {
            MethodInvoker action = delegate
            {
                for (int i = 0; i < _list.Count; i++)
                {
                    PlayerStruct st = (PlayerStruct)_list[i];
                    if (st != null)
                    {
                        ((Label)m_arrLabelPlayerLabel[st.m_id - 1]).Visible = true;
                        ((Label)m_arrLabelPlayerName[st.m_id - 1]).Visible = true;
                        ((ComboBox)m_arrCbxPlayerType[st.m_id - 1]).Visible = true;
                        ((ComboBox)m_arrCbxPlayerType[st.m_id - 1]).Enabled = false;

                        ((Label)m_arrLabelPlayerName[st.m_id - 1]).Text = st.m_name;
                        ((ComboBox)m_arrCbxPlayerType[st.m_id - 1]).SelectedIndex = (int)st.m_playerType;
                    }
                }
            };
            this.BeginInvoke(action);
        }

        #endregion Client part

        /// <summary>
        /// Update state of the controls 
        /// </summary>
        /// <param name="listening"></param>
        private void UpdateControls(bool listening)
        {
            if (m_multiType == MULTI_PLAYER_TYPE.MULTI_PLAYER_SERVER)
            {
                btnCreateServer.Enabled = !listening;
                btnStopServer.Enabled = listening;
            }
            else if (m_multiType == MULTI_PLAYER_TYPE.MULTI_PLAYER_CLIENT)
            {
                btnConnectServer.Enabled = !listening;
                btnDisconnectFromServer.Enabled = listening;
            }
        }

        /// <summary>
        /// Display a message in this dialog
        /// </summary>
        /// <param name="_str"></param>
        public void UpdateMsg(String _str, Boolean _noTimer = false)
        {
            MethodInvoker action = delegate
            {
                if (!_noTimer)
                {
                    m_msgTimer.Enabled = true;
                    m_msgTimer.Start();
                }
                lbMessage.Text = _str;
                lbMessage.Visible = true;
                lbMessage.BringToFront();
            };
            lbMessage.BeginInvoke(action);
        }

        /// <summary>
        /// Stop display a message in this dialog
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void msgTimer_Tick(object sender, EventArgs e)
        {
            m_msgTimer.Stop();
            lbMessage.Visible = false;
        }

        /// <summary>
        /// Send message from/to server/client
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendMsg_Click(object sender, EventArgs e)
        {
            String str = txtSendMsg.Text;
            m_multiGame.SendMessage(str);
        }

        /// <summary>
        /// Start Multi game
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStartGame_Click(object sender, EventArgs e)
        {
            if (this.m_multiType == MULTI_PLAYER_TYPE.MULTI_PLAYER_SERVER)
            {
                m_isStartedGame = true;

                while (m_availableId.Count > 0)
                {
                    ((ComboBox)m_arrCbxPlayerType[(int)m_availableId[0] - 1]).SelectedIndex = (int)PLAYER_TYPE.COMPUTER;
                }

                this.Hide();
            }
            else
            {
                UpdateMsg("Vui lòng đợi game bắt đầu", true);
            }
        }

        /// <summary>
        /// Cancel creating a multi game
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCancelGame_Click(object sender, EventArgs e)
        {
            Disconnect();
            this.Dispose();
        }

        String m_testText = null;

        public void UpdateTestText(String txt)
        {
            try
            {
                MethodInvoker action = delegate
                {
                    rtxTest.AppendText(txt);
                    m_testText += txt;
                };

                rtxTest.BeginInvoke(action);
            }
            catch (SystemException e)// System.InvalidOperationException)
            {
                string estr = e.ToString();
                UpdateMsg(estr);
            }
        }

        private void cbPlayerType_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (m_bFormLoaded)
            {
                ComboBox cb = (ComboBox)sender;
                int id = Int32.Parse(cb.ValueMember);

                if (id > 1)
                {
                    if (cb.SelectedIndex == (int)PLAYER_TYPE.HUMAN)
                    {
                        if (!m_availableId.Contains(id))
                        {
                            m_availableId.Add(id);
                        }
                    }
                    else
                    {
                        m_availableId.Remove(id);
                    }

                    ArrayUtil.ASCSortArrayListInt(m_availableId);
                }
            }
        }

        private void FrmCreateMultiGame_FormClosed(object sender, FormClosedEventArgs e)
        {
        }

        private void FrmCreateMultiGame_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (!m_isStartedGame)
            {
                Disconnect();                
            }
        }

        private void Disconnect()
        {
            m_multiGame.Stop();
            if (m_multiType == MULTI_PLAYER_TYPE.MULTI_PLAYER_CLIENT)
            {
                if (m_gamePlayerId > -1)
                {
                    ((Label)m_arrLabelPlayerName[m_gamePlayerId - 1]).Text = "";
                    m_gamePlayerId = -1;
                }
            }
        }
        
        /// <summary>
        /// On receiving a move command, delegate to the Stage
        /// </summary>
        /// <param name="_id"></param>
        /// <param name="_arrCards"></param>
        /// <param name="_isServer"></param>
        public void OnReceiveMove(int _id, int[] _arrCards, Boolean _isServer)
        {
            try
            {
                //this.Invoke((MethodInvoker)delegate()
                //{
                //});
                MethodInvoker action = delegate
                {
                    MainFrame.m_insState.OnReceiveMove(_id, _arrCards, _isServer);
                };
                MainFrame.m_insState.BeginInvoke(action);
            }
            catch (SystemException se)
            {
                MessageBox.Show(se.Message);
            }
        }

        /// <summary>
        /// On receiving a skip command, delegate to the Stage
        /// </summary>
        /// <param name="_id"></param>
        /// <param name="_isServer"></param>
        public void OnReceiveSkip(int _id, Boolean _isServer)
        {
            try
            {
                MethodInvoker action = delegate
                {
                    MainFrame.m_insState.OnReceiveSkip(_id, _isServer);
                };
                MainFrame.m_insState.BeginInvoke(action);
            }
            catch (SystemException se)
            {
                MessageBox.Show(se.Message);
            }
        }

        /// <summary>
        /// Start game
        /// </summary>
        /// <param name="_arrCards"></param>
        /// <param name="_mIndex"></param>
        public void StartGame(int[] _arrCards, int _mIndex)
        {
            MethodInvoker action = delegate
            {
                m_pcArrayCards = _arrCards;
                m_pcIndex = _mIndex;
                m_isStartedGame = true;
                lbMessage.Visible = false;

                this.Hide();
            };
            this.BeginInvoke(action);
        }

        /// <summary>
        /// Continue game
        /// </summary>
        /// <param name="_arrCards"></param>
        /// <param name="_mIndex"></param>
        public void ContinueGame(int[] _arrCards, int _mIndex)
        {
            try
            {
                MethodInvoker action = delegate
                {
                    m_pcArrayCards = _arrCards;
                    m_pcIndex = _mIndex;
                    MainFrame.m_insState.OnContinueGame();
                };
                MainFrame.m_insState.BeginInvoke(action);
            }
            catch (SystemException se)
            {
                MessageBox.Show(se.Message);
            }
        }

    }
}
